package com.tping.queue.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * 启动10个线程
 * 库存 6
 * 生成一个合并队列
 * 每个用户能够拿到注解的请求响应
 *
 * @author xiai
 * @Date 2023/2/20 17:51
 * @Version 1.0
 */
public class ThreadQueueDemo {

    /**
     * 库存数量
     */
    private Integer stock;
    /**
     * 请求线程数量
     */
    private Integer threadNum;
    /**
     * 合并队列长度
     */
    private Integer queueSize;

    /**
     * 阻塞队列
     */
    private BlockingQueue<RequestPromise> blockingQueue;

    public ThreadQueueDemo(Integer stock, Integer threadNum, Integer queueSize) {
        this.stock = stock;
        this.threadNum = threadNum;
        this.queueSize = queueSize;
        this.blockingQueue = new LinkedBlockingQueue<>(queueSize);
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadQueueDemo threadQueue = new ThreadQueueDemo(6, 10, 10);
        ExecutorService executor = Executors.newCachedThreadPool();
        threadQueue.mergeJob();

        List<Future<Result>> futureList = new ArrayList<>();
        CountDownLatch countDown = new CountDownLatch(threadQueue.threadNum);

        for (int i = 0; i < threadQueue.threadNum; i++) {
            Long orderId = 100L + i;
            String userId = "U" + Long.valueOf(orderId);

            Future<Result> future = executor.submit(() -> {
                countDown.countDown();
                return threadQueue.operate(new UserRequest(orderId, userId, 1));
            });

            futureList.add(future);
        }

        for (Future<Result> future : futureList) {
            try {
                Result result = future.get(300, TimeUnit.MILLISECONDS);
                System.out.println(Thread.currentThread().getName() + " 客户端请求响应明细：" + result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 用户库存扣减
     *
     * @param request
     * @return
     */
    public Result operate(UserRequest request) throws InterruptedException {
        // TODO: 请求添加到阻塞队列中
        RequestPromise requestPromise = new RequestPromise(request);
        boolean enqueueSuccess = blockingQueue.offer(requestPromise, 100, TimeUnit.MILLISECONDS);
        if( !enqueueSuccess ){
            return new Result(false, "系统繁忙...");
        }

        synchronized (requestPromise) {
            try {
                requestPromise.wait(200);
            } catch (InterruptedException e) {
                return new Result(false, "等待超时");
            }
        }

        return requestPromise.getResult();
    }

    public void mergeJob() {
        new Thread(() -> {
            List<RequestPromise> list = new ArrayList<>();
            while (true){
                if( blockingQueue.isEmpty() ){
                    try {
                        Thread.sleep(10);
                        continue;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                while ( blockingQueue.peek() != null ){
                    list.add(blockingQueue.poll());
                }

                System.out.println(Thread.currentThread().getName() + " :合并扣减线程：" + list);

                int sum = list.stream().mapToInt(e -> e.getRequest().getCount()).sum();
                // 两种情况
                if( sum <= stock ){
                    stock -= sum;

                    // notify user
                    list.forEach(requestPromise -> {
                        requestPromise.setResult(new Result(true, "OK"));
                        synchronized (requestPromise) {
                            requestPromise.notify();
                        }
                    });
                    continue;
                }

                // 单个线程去扣减库存
                for (RequestPromise requestPromise : list) {
                    Integer count = requestPromise.getRequest().getCount();
                    if( count <= stock ){
                        stock -= count;
                        requestPromise.setResult(new Result(true, "OK"));
                        synchronized (requestPromise) {
                            requestPromise.notify();
                        }
                    } else {
                        requestPromise.setResult(new Result(false, "库存不足"));
                    }
                }

                list.clear();
            }
        }, "mergeThread").start();
    }
}

class RequestPromise {
    private UserRequest request;

    private Result result;

    public RequestPromise(UserRequest request) {
        this.request = request;
    }

    public RequestPromise(UserRequest request, Result result) {
        this.request = request;
        this.result = result;
    }

    public UserRequest getRequest() {
        return request;
    }

    public void setRequest(UserRequest request) {
        this.request = request;
    }

    public Result getResult() {
        return result;
    }

    public void setResult(Result result) {
        this.result = result;
    }

    @Override
    public String toString() {
        return "RequestPromise{" +
                "request=" + request +
                ", result=" + result +
                '}';
    }
}

/**
 * 请求响应
 */
class Result {
    private boolean success;

    private String msg;

    public Result(boolean success, String msg) {
        this.success = success;
        this.msg = msg;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "Result{" +
                "success=" + success +
                ", msg='" + msg + '\'' +
                '}';
    }
}

/**
 * 请求信息
 */
class UserRequest {

    // 订单编号
    private Long orderId;
    // 用户编号
    private String userId;
    // 扣减库存数量
    private Integer count;

    public UserRequest(Long orderId, String userId, Integer count) {
        this.orderId = orderId;
        this.userId = userId;
        this.count = count;
    }

    public Long getOrderId() {
        return orderId;
    }

    public void setOrderId(Long orderId) {
        this.orderId = orderId;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    @Override
    public String toString() {
        return "UserRequest{" +
                "orderId=" + orderId +
                ", userId='" + userId + '\'' +
                ", count=" + count +
                '}';
    }
}
